import {withLock} from "easylock";
import * as Redis from "ioredis"
import {isNullOrUndefined} from "util";
import GM, {generateHash} from "../../../database/models/gm"
import GMSession from '../../../database/models/gmSession'
import {GmWithdrawModel} from "../../../database/models/gmWithdraw";
import {PlayerModel} from "../../../database/models/player";
import {RechargeOrderModel} from "../../../database/models/rechargeOrder";
import GMRecord from '../../../database/models/record'
import moment = require("moment");

export const typeDefs = `
type GM{
  _id: String!
  username: String!
  role:String!
  gold:Int
  gem: Int
  spendGold:Int
  spendGem:Int
  relation: [GM!]
  inviteCode: String
  cashKickback: Float
  gemKickback: Int
  remarks: String
  }

type SubGmWithInvited{
gms: [GM!]
invited: [Player!]
}

input CreateGM{
username: String!
  remarks: String
  password: String!
  passwordConfirm: String!
  role:String!
  gold: Int
  gem: Int
  inviteCode: String
  createAt: String!
  }

type GmWithdraw{
_id:String!
gm:GM
commission:Float!
state: String!
createAt: String!
finishedAt: String
}
`

export const queryDefs = `
  myProfile: GM
  gmProfile(_id:String!): GM
  gmList(page:Int,searchText:String):[GM!]
  gmInvitedPlayers(_id:String!):[Player!]
  gmRechargeOrder(_id:String!,start:String!,end:String!):[RechargeOrder]
  getSubGmWithInvited(_id: String!):SubGmWithInvited!
  getMyWithdraws(start: String, end: String):[GmWithdraw]!
  getAllWithdraws(state: String, start: String, end: String):[GmWithdraw]!
`

export const mutationDefs = `
  setGmPartner(_id:String!,isPartner: Boolean!):GM
  changePassword(_id:String!,newPassword: String!):GM
  changeRemarks(_id:String!,newRemarks: String!):GM
  rechargeGM(_id:String!,gem: Int!):GM
  createGM(gmData:CreateGM):GM
  changePasswordBySelf(newPassword:String!,oldPassword:String!):GM
  deleteGM(_id:String!): Boolean
  requestWithdraw:GmWithdraw
  dealWithdraw(_id:String!):GmWithdraw
`

const redisClient = new Redis()

const gmLevelMap = {
  super: 99,
  level1: 10,
  level2: 9
}

export const resolvers = {
  Query: {
    async myProfile(_, args, context) {
      const authedUser = context.request.user

      if (authedUser) {
        const gm = await GM.findById(context.request.user._id).lean()
        if (gm.role !== `super`)
          delete gm[`remarks`]
        return gm
      } else {
        throw Error('Unauthorized')
      }
    },
    async gmProfile(_, {_id}, context) {
      const authedUser = context.request.user

      let gm = null
      if (authedUser) {
        const id = authedUser.role === `super` ? _id : context.request.user._id
        gm = await GM.findById(id).lean()

        if (authedUser.role !== `super`) {
          delete gm['remarks']
        }
        if (gm) {
          return gm
        } else {
          throw Error('无此用户')
        }
      } else {
        throw Error('Unauthorized')
      }
    },
    async gmList(_, {page = 0, searchText}, context) {
      const authedUser = context.request.user
      if (authedUser.role === 'super') {
        let gms = null
        if (searchText) {
          /*const chars = searchText.split('').filter(c => c.trim())

          const zsets = chars.map(c => `gm:${c}`)

          const batch = redisClient.pipeline()

          const destination = 'dest' + uuid();
          const res = await batch.zinterstore(destination, zsets.length, zsets)
            .zrevrangebyscore(destination, '+inf', '-inf', 'limit', 0, 10)
            .del(destination)
            .exec()

          const hitedId = res[1][1] || []

          if (hitedId.length === 0) return []

          return GM.find({_id: {$in: hitedId}}).lean().exec()*/
          const reg = new RegExp(searchText, 'ig')
          gms = await GM.find({username: reg})
        } else {
          gms = await GM.find().skip(page * 20).limit(20)
        }

        return gms
      } else {

        return []
      }
    },
    gmInvitedPlayers: async (_, {_id}, context) => {
      const authedUser = context.request.user

      if (authedUser.role === 'super') {
        const players = await PlayerModel.find({inviteBy: _id}).lean()
        return players
      } else {
        return PlayerModel.find({inviteBy: authedUser._id}).lean()
      }
    },
    gmRechargeOrder: async (_, {_id, start, end}, context) => {
      const authedUser = context.request.user
      const fromDate = moment(start).startOf('day').toDate()
      const toDate = moment(end).endOf('day').toDate()

      const inviter = authedUser.role === 'super' ? _id : authedUser._id

      const orders = await RechargeOrderModel.find({
        inviteBy: inviter,
        createAt: {$gt: fromDate, $lt: toDate},
        state: 'paid'
      }).populate(`player`)
        .sort({
          createAt: -1
        })
        .lean()
      return orders
    },
    getSubGmWithInvited: async (_, {_id}, context) => {
      const authedUser = context.request.user
      const queryGm = await GM.findById(_id).lean()

      if (!authedUser)
        throw Error(`未登录`)

      if (gmLevelMap[queryGm.role] > gmLevelMap[authedUser.role])
        throw Error(`你没有权限`)

      const isSuper = authedUser.role === `super`
      const invited = await PlayerModel.find({inviteBy: queryGm}).lean()
      const subGMs = await GM.find({superior: queryGm}).lean()
      if (!isSuper)
        subGMs.forEach((g) => {
          delete g['remarks']
        })

      return {invited, gms: subGMs}
    },
    getMyWithdraws: async (_, {start, end}, context) => {
      const authedUser = context.request.user
      if (!authedUser)
        throw Error(`没有权限`)

      let draws = null
      if (!isNullOrUndefined(start) && !isNullOrUndefined(end)) {
        const fromDate = moment(start).startOf('day').toDate()
        const toDate = moment(end).endOf('day').toDate()
        const createAt = {$gt: fromDate, $lt: toDate}
        draws = await GmWithdrawModel.find({gm: authedUser._id, createAt}).sort({createAt: -1}).populate(`gm`).lean()
      } else {
        draws = await GmWithdrawModel.find({gm: authedUser._id}).sort({createAt: -1}).populate(`gm`).lean()
      }
      if (authedUser.role !== `super` && draws.length !== 0) {
        draws.forEach(draw => {
          draw.gm.remarks = ``
        })
      }
      return draws
    },
    getAllWithdraws: async (_, {state, start, end}, context) => {
      const authedUser = context.request.user
      if (!authedUser || authedUser.role !== `super`)
        throw Error(`没有权限`)

      if (!isNullOrUndefined(start) && !isNullOrUndefined(end)) {
        const fromDate = moment(start).startOf('day').toDate()
        const toDate = moment(end).endOf('day').toDate()
        const createAt = {$gt: fromDate, $lt: toDate}
        const draws = await GmWithdrawModel.find({createAt}).sort({createAt: -1}).populate(`gm`).lean()
        return draws
      } else {
        const draws = await GmWithdrawModel.find({state}).sort({createAt: -1}).populate(`gm`).lean()
        return draws
      }
    },
  },
  Mutation: {
    createGM: async (_, {gmData}, context) => {
      const username = gmData.username
      const password = gmData.password
      const inviteCode = gmData.inviteCode !== '' && gmData.inviteCode.length < 6 ? null : gmData.inviteCode
      if (inviteCode === null) {
        throw Error('inviteCode illegal')
      }
      const createAt = new Date(gmData.createAt)

      const authedUser = context.request.user

      let allowedLevel = ''
      if (authedUser.role === 'super') {
        allowedLevel = 'level1'
      } else if (authedUser.role === 'level1') {
        allowedLevel = 'level2'
      } else {
        throw Error('level2 cant create GM')
      }

      const exist = await GM.findOne({username})
      if (exist) {
        throw Error('用户名已存在')
      }
      const codeExist = await GM.findOne({inviteCode})
      if (codeExist) {
        throw Error('该邀请码已存在')
      }

      const pw = generateHash(password)
      const gm = new GM({
        username,
        password: pw,
        role: allowedLevel,
        superior: authedUser,
        relation: authedUser.relation,
        createAt,
        inviteCode
      });
      gm.relation = [gm.id].concat(gm.relation)
      await gm.save()
      return gm
    },
    deleteGM: async (_, {_id}, context) => {
      const authedUser = context.request.user
      if (authedUser.role === 'super') {
        const gm = await GM.findById(_id)

        if (gm.role === 'super') {
          throw Error('不能删除超级管理员')
        }

        await GM.remove({_id})
        return true
      } else {
        throw Error('只有超级管理员才能删除')
      }
    },

    async setGmPartner(_, {_id, isPartner}, context) {
      const authedUser = context.request.user

      if (authedUser.role === 'super') {
        const gm = await GM.findById(_id)
        gm.partner = isPartner
        await gm.save()
        return gm
      } else {
        return null
      }
    },
    changePassword: async (_, {_id, newPassword}, context) => {
      const authedUser = context.request.user

      if (authedUser.role === 'super') {
        const gm = await GM.findById(_id)

        if (gm.role === 'super') {
          throw Error('后台不能修改管理员超级密码')
        }
        const hashedPW = (GM as any).generateHash(newPassword)
        gm.password = hashedPW
        await gm.save()
        return gm
      } else {
        throw Error('只有超级管理员才能修改密码')
      }
    },
    changeRemarks: async (_, {_id, newRemarks}, context) => {
      const authedUser = context.request.user

      if (authedUser.role === 'super') {
        const gm = await GM.findById(_id)

        if (gm.role === 'super') {
          throw Error('后台不能修改管理员超级备注')
        }
        gm.remarks = newRemarks
        await gm.save()
        return gm
      } else {
        throw Error('只有超级管理员才能修改备注')
      }
    },
    changePasswordBySelf: async (_, {oldPassword, newPassword}, context) => {
      const authedUser = context.request.user

      if (authedUser) {
        const gm = await GM.findById(authedUser._id)

        if (gm.role === 'super') {
          throw Error('后台不能修改管理员超级密码')
        }

        const hashedNewPW = (GM as any).generateHash(newPassword)

        if (!gm.validPassword(oldPassword)) {

          throw Error('当前密码错误')
        }

        gm.password = hashedNewPW
        await gm.save()
        await GMSession.remove({"session.userId": gm._id.toString()})

        return gm
      } else {
        throw Error('请重新登录')
      }
    },
    async rechargeGM(_, {_id, gem}, context) {
      const authedUser = context.request.user

      if (authedUser.role === 'super') {
        let gm = await GM.findById(_id)

        if (!gm) {
          throw Error('没有此代理')
        }

        gm = await GM.findOneAndUpdate({_id}, {$inc: {gem}}, {new: true})
        await GM.update({_id: authedUser._id},
          {$inc: {spendGem: gem, gem: -gem}})
        await new GMRecord({
          from: authedUser._id,
          to: _id, amount: gem,
          relation: [authedUser._id]
        }).save()

        return gm
      } else {
        throw Error('只有超级管理员才能充值')
      }
    },
    requestWithdraw: async (_, {}, context) => {
      const authedUser = context.request.user

      if (!authedUser)
        throw Error(`老哥你没有权限`)

      if (authedUser.cashKickback < 500)
        throw Error(`返利超过500元方可申请提现`)

      const req = await withLock(authedUser._id.toString(), async () => {
        const request = await GmWithdrawModel.findOne({gm: authedUser._id, state: 'created'})
        if (request)
          throw Error('请等待上次申请处理完成')
        const back = authedUser.cashKickback
        await GM.findByIdAndUpdate(authedUser._id, {$inc: {cashKickback: -back}})
        const r = new GmWithdrawModel({
          gm: authedUser._id,
          state: `created`,
          commission: back,
          createAt: new Date()
        })
        await r.save()
        return r
      })
      return req
    },
    dealWithdraw: async (_, {_id}, context) => {
      const authedUser = context.request.user

      if (!authedUser || authedUser.role !== `super`)
        throw Error(`老哥你没有权限`)

      const withdraw = await GmWithdrawModel.findById(_id).populate(`gm`).lean()
      if (!withdraw || withdraw.state !== `created`) throw Error(`订单不存在或者已经处理完成`)
      if (!withdraw.gm) throw Error(`提现者不存在`)

      const draw = await GmWithdrawModel.findByIdAndUpdate(_id, {
        $set: {
          state: `finished`,
          finishedAt: new Date()
        }
      }, {new: true}).lean()
      if (!draw) throw Error(`提现异常`)
      return draw
    },
  }
}
